/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/cat.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../data_struct/head.h"
#include "../config/head.h"
#include "../impl/head.h"
#include "../impl/head.h"
#include "../msg/head.h"
#include "msg_aio.h"
#include "aio_udp.h"

namespace cat
{

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    aio_udp::aio_udp()
    {
    }

    aio_udp::~aio_udp()
    {
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // net ret msg proc

    int aio_udp::proc( xos::i_msg *& pMsg )
    {
        int ret = 1;

        int nMsg = pMsg->get_int( 0, 0 );

        switch( nMsg )
        {
            // 
            // udp
            // 
        case xos_aio::i_aio::AIO_UDP_INIT_RET:
            {
                on_init( pMsg );
            }
            break;
        case xos_aio::i_aio::AIO_UDP_RECV_RET:
            {
                on_recv( pMsg );
            }
            break;
        case xos_aio::i_aio::AIO_UDP_SEND_RET:
            {
                on_send( pMsg );
            }
            break;
        case xos_aio::i_aio::AIO_UDP_CLOSE_RET:
            {
                on_close( pMsg );
            }
            break;
        default:
            {
                ret = 0;
            }
            break;
        }

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // udp opt

    // 
    // m_bData[0] : true or false for result
    // 
    // m_nData[0] : cmd type
    // 
    // m_nData[1] : local port
    // m_szStr[1] : local ip
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::post_init( connection * pConnect )
    {
        int ret = 0;

        udp * pUdp = pConnect->m_pUdp;
        xos::i_msg * pMsg = mgr::xos()->msg();
        bool bCannot = !pConnect || !pConnect->none() || !pConnect->is_udp() || !( state::running() || state::starting() );

        if( ( 0 == ret ) && bCannot )
        {
            pMsg->set_int( 0, NET_UDP_INIT );
            pMsg->set_bool( 0, false );
            pMsg->set_void( 0, pConnect );
            pMsg->set_str( 0, "test failed" );
            msg::notify_net( pMsg, false );
            ret = 1;
        }

        if( 0 == ret )
        {
            pMsg->set_int( 0, xos_aio::i_aio::AIO_UDP_INIT );
            pMsg->set_str( 1, pUdp->m_ip.c_str() );
            pMsg->set_int( 1, pUdp->m_nPort );
            pMsg->set_void( 0, pConnect );
        }

        if( 0 == ret )
        {
            pConnect->set_initing();
            ret = msg_aio::get()->post_request( pMsg );
        }

        return ret;
    }

    // 
    // m_bData[0] : true or false for opt result
    // 
    // m_nData[0] : cmd type
    // 
    // m_nData[2] : peer port
    // m_szStr[2] : peer ip
    // 
    // m_nData[3] : recv len
    // m_lpBuf[0] : recv buf
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::post_recv( connection * pConnect )
    {
        int ret = 0;

        udp * pUdp = pConnect->m_pUdp;
        xos::i_msg * pMsg = 0;
        xos::i_buf * pBuf = 0;
        bool bCannot = !pConnect || !pConnect->running() || !pConnect->is_udp() || !( state::running() || state::starting() );

        if( ( 0 == ret ) && bCannot )
        {
            ret = 1;
        }

        if( 0 == ret )
        {
            pMsg = mgr::xos()->msg();
            pBuf = mgr::xos()->buf();
        }

        if( 0 == ret )
        {
            pMsg->set_int( 0, xos_aio::i_aio::AIO_UDP_RECV );
            pMsg->set_buf( 0, pBuf );
            pMsg->set_void( 0, pConnect );
            pMsg->set_void( 1, pUdp->m_pAioKey );
            pBuf = 0;
        }

        if( 0 == ret )
        {
            pUdp->m_nPostRecvNum++;
            ret = msg_aio::get()->post_request( pMsg );
        }

        return ret;
    }

    // 
    // m_bData[0] : true or false for opt result
    // 
    // m_nData[0] : cmd type
    // 
    // m_nData[2] : peer port
    // m_szStr[2] : peer ip
    // 
    // m_nData[4] : send len
    // m_lpBuf[0] : send buf
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::post_send( connection * pConnect, xos::i_list * pList, const char * lpszIp, int nPort )
    {
        int ret = 0;

        xos::i_buf * pBuf = 0;

        while( ( pBuf = ( xos::i_buf * )pList->front( 0 ) ) )
        {
            post_send( pConnect, pBuf, lpszIp, nPort );
            pList->pop_front();
        }

        return ret;
    }

    // 
    // m_bData[0] : true or false for opt result
    // 
    // m_nData[0] : cmd type
    // 
    // m_nData[2] : peer port
    // m_szStr[2] : peer ip
    // 
    // m_nData[4] : send len
    // m_lpBuf[0] : send buf
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::post_send( connection * pConnect, xos::i_buf *& pBuf, const char * lpszIp, int nPort )
    {
        int ret = 0;

        udp * pUdp = pConnect->m_pUdp;
        xos::i_msg * pMsg = mgr::xos()->msg();
        bool bCannot = !pConnect || !pConnect->running() || !pConnect->is_udp() || !state::running();

        if( ( 0 == ret ) && bCannot )
        {
            pMsg->set_int( 0, NET_UDP_SEND );
            pMsg->set_bool( 0, false );
            pMsg->set_void( 0, pConnect );
            pMsg->set_buf( 0, pBuf );
            pMsg->set_str( 0, "test failed" );
            msg::notify_net( pMsg, false );
            pBuf = 0;
            ret = 1;
        }

        if( 0 == ret )
        {
            pMsg->set_int( 0, xos_aio::i_aio::AIO_UDP_SEND );
            pMsg->set_str( 2, lpszIp );
            pMsg->set_int( 2, nPort );
            pMsg->set_buf( 0, pBuf );
            pMsg->set_void( 0, pConnect );
            pMsg->set_void( 1, pUdp->m_pAioKey );
            pBuf = 0;
        }

        if( 0 == ret )
        {
            pUdp->m_nPostSendNum++;
            ret = msg_aio::get()->post_request( pMsg );
        }

        return ret;
    }

    // 
    // m_nData[0] : cmd type
    // m_nData[1] : how
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::post_shut_down( connection * pConnect, int how )
    {
        int ret = 0;

        udp * pUdp = pConnect->m_pUdp;
        xos::i_msg * pMsg = mgr::xos()->msg();
        bool bCannot = !pConnect || !pConnect->running() || !pConnect->is_udp() || !( state::running() || state::quitting() );

        if( ( 0 == ret ) && bCannot )
        {
            pMsg->set_int( 0, NET_UDP_SHUTDOWN );
            pMsg->set_int( 1, how );
            pMsg->set_bool( 0, false );
            pMsg->set_void( 0, pConnect );
            pMsg->set_str( 0, "test failed" );
            msg::notify_net( pMsg, false );
            ret = 1;
        }

        if( 0 == ret )
        {
            pMsg->set_int( 0, xos_aio::i_aio::AIO_UDP_SHUT_DOWN );
            pMsg->set_void( 0, pConnect );
            pMsg->set_void( 1, pUdp->m_pAioKey );
            pMsg->set_int( 1, how );
        }

        if( 0 == ret )
        {
            ret = msg_aio::get()->post_request( pMsg );
        }

        return ret;
    }

    // 
    // m_bData[0] : true or false for opt result
    // 
    // m_nData[0] : cmd type
    // m_nData[1] : linger.l_onoff
    // m_nData[2] : linger.l_linger
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::post_close( connection * pConnect, int onoff, int linger )
    {
        int ret = 0;

        udp * pUdp = pConnect->m_pUdp;
        xos::i_msg * pMsg = mgr::xos()->msg();
        bool bCannot = !pConnect || pConnect->closing() || !pConnect->is_udp() || !( state::running() || state::quitting() );

        if( ( 0 == ret ) && bCannot )
        {
            pMsg->set_int( 0, NET_UDP_CLOSE );
            pMsg->set_int( 1, onoff );
            pMsg->set_int( 2, linger );
            pMsg->set_bool( 0, false );
            pMsg->set_void( 0, pConnect );
            pMsg->set_str( 0, "test failed" );
            msg::notify_net( pMsg, false );
            ret = 1;
        }

        if( 0 == ret )
        {
            pMsg->set_int( 0, xos_aio::i_aio::AIO_UDP_CLOSE );
            pMsg->set_int( 1, onoff );
            pMsg->set_int( 2, linger );
            pMsg->set_void( 0, pConnect );
            pMsg->set_void( 1, pUdp->m_pAioKey );
        }

        if( 0 == ret )
        {
            pConnect->set_closing();
            ret = msg_aio::get()->post_request( pMsg );
        }

        return ret;
    }

    int aio_udp::close_test( connection * pConnect )
    {
        int ret = 0;

        udp * pUdp = pConnect->m_pUdp;

        bool bNeedClose = pConnect->running() && ( 0 == pUdp->m_nPostRecvNum ) && ( 0 == pUdp->m_nPostSendNum );
        if( bNeedClose )
        {
            pConnect->set_need_close();
            xos::i_msg * pMsg = mgr::xos()->msg();
            pMsg->set_int( 0, NET_UDP_NEED_CLOSE );
            pMsg->set_void( 0, pConnect );
            msg::notify_net( pMsg, false );
        }

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // udp

    // 
    // m_bData[0] : true or false for result
    // 
    // m_nData[0] : cmd type
    // 
    // m_nData[1] : local port
    // m_szStr[1] : local ip
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::on_init( xos::i_msg *& pMsg )
    {
        int ret = 0;

        connection * pConnect = (connection *)pMsg->get_void( 0, 0 );
        udp * pUdp = pConnect->m_pUdp;
        void * pAioKey = pMsg->get_void( 1, 0 );
        bool bRet = pMsg->get_bool( 0, 0 );
        int nPostRecv = 0;

        if( bRet )
        {
            pUdp->m_ip = pMsg->get_str( 1, 0, 0, 0 );
            pUdp->m_nPort = pMsg->get_int( 1, 0 );
            connection::list()->push_front( pConnect );
            pConnect->m_connection_iter = connection::list()->begin();
            pUdp->m_pAioKey = pAioKey;
        }
        if( bRet )
        {
            pConnect->set_running();
            pConnect->lock_server();
        }
        {
            pMsg->set_int( 0, NET_UDP_INIT );
            msg::notify_net( pMsg, false );
        }
        while( bRet && ( 0 == nPostRecv ) && ( pUdp->m_nPostRecvNum < config::get()->tcp_max_post_recv ) )
        {
            nPostRecv = post_recv( pConnect );
        }

        return ret;
    }

    // 
    // m_bData[0] : true or false for opt result
    // 
    // m_nData[0] : cmd type
    // 
    // m_nData[2] : peer port
    // m_szStr[2] : peer ip
    // 
    // m_nData[3] : recv len
    // m_lpBuf[0] : recv buf
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::on_recv( xos::i_msg *& pMsg )
    {
        int ret = 0;

        connection * pConnect = (connection *)pMsg->get_void( 0, 0 );
        xos::i_buf * pBuf = pMsg->get_buf( 0, 0 );
        udp * pUdp = pConnect->m_pUdp;
        bool bRet = pMsg->get_bool( 0, 0 );
        int nPostRecv = 0;

        if( bRet && state::running() && pConnect->running() && !pConnect->m_pQuitAfterSend && !pConnect->m_pCloseAfterSend )
        {
            pConnect->net_timeout_ms = 0;
            pConnect->lock_server();
            pMsg->set_int( 0, NET_UDP_RECV );
            msg::notify_net( pMsg, false );
            pBuf = 0;
        }

        xos_stl::release_interface( pBuf );
        pUdp->m_nPostRecvNum--;

        while( bRet && ( 0 == nPostRecv ) && ( pUdp->m_nPostRecvNum < config::get()->tcp_max_post_recv ) )
        {
            nPostRecv = post_recv( pConnect );
        }

        close_test( pConnect );

        return ret;
    }

    // 
    // m_bData[0] : true or false for opt result
    // 
    // m_nData[0] : cmd type
    // 
    // m_nData[2] : peer port
    // m_szStr[2] : peer ip
    // 
    // m_nData[4] : send len
    // m_lpBuf[0] : send buf
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::on_send( xos::i_msg *& pMsg )
    {
        int ret = 0;

        connection * pConnect = (connection *)pMsg->get_void( 0, 0 );
        udp * pUdp = pConnect->m_pUdp;

        pUdp->m_nPostSendNum--;

        {
            pMsg->set_int( 0, NET_UDP_SEND );
            msg::notify_net( pMsg, false );
        }

        close_test( pConnect );

        return ret;
    }

    // 
    // m_bData[0] : true or false for opt result
    // 
    // m_nData[0] : cmd type
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::on_close( xos::i_msg *& pMsg )
    {
        int ret = 0;

        {
            pMsg->set_int( 0, NET_UDP_CLOSE );
            msg::notify_net( pMsg, false );
        }

        return ret;
    }

} // cat
